package net.xdclass.service.impl;

import lombok.extern.slf4j.Slf4j;
import net.xdclass.component.ShortLinkComponent;
import net.xdclass.config.RabbitMQConfig;
import net.xdclass.controller.Request.ShortLinkAddRequest;
import net.xdclass.controller.Request.ShortLinkDelRequest;
import net.xdclass.controller.Request.ShortLinkPageRequest;
import net.xdclass.controller.Request.ShortLinkUpdateRequest;
import net.xdclass.enums.DomainTypeEnum;
import net.xdclass.enums.EventMessageType;
import net.xdclass.enums.ShortLinkStateEnum;
import net.xdclass.interceptor.LoginInterceptor;
import net.xdclass.manager.DomainManager;
import net.xdclass.manager.GroupCodeMappingManager;
import net.xdclass.manager.LinkGroupManager;
import net.xdclass.manager.ShortLinkManager;
import net.xdclass.model.*;
import net.xdclass.mapper.ShortLinkMapper;
import net.xdclass.service.ShortLinkService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import net.xdclass.util.CommonUtil;
import net.xdclass.util.IdUtil;
import net.xdclass.util.JsonData;
import net.xdclass.util.JsonUtil;
import net.xdclass.vo.ShortLinkVO;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import java.util.Arrays;
import java.util.Map;

/**
 * <p>
 *  服务实现类
 * </p>
 * @author 二当家小D
 * @since 2025-08-15
 */
@Service
@Slf4j
public class ShortLinkServiceImpl extends ServiceImpl<ShortLinkMapper, ShortLinkDO> implements ShortLinkService {
	@Autowired
	private ShortLinkManager shortLinkManager;
	@Autowired
	private RabbitTemplate rabbitTemplate;
	@Autowired
	private RabbitMQConfig rabbitMQConfig;
	@Autowired
	private DomainManager domainManager;
	@Autowired
	private LinkGroupManager linkGroupManager;
	@Autowired
	private RedisTemplate redisTemplate;
	@Autowired
	private GroupCodeMappingManager groupCodeMappingManager;
	@Autowired
	private ShortLinkComponent shortLinkComponent;

	@Override
	public ShortLinkVO getByShortLink(String shortLink) {
		ShortLinkDO shortLinkDO = shortLinkManager.findByShortLinkCode(shortLink);
		ShortLinkVO shortLinkVO = new ShortLinkVO();
		BeanUtils.copyProperties(shortLinkDO,shortLinkVO);
		return shortLinkVO;
	}

	@Override
	public JsonData addShortLink(ShortLinkAddRequest shortLinkAddRequest){
		Long accountNo = LoginInterceptor.threadLocal.get().getAccountNo();
		String newUrl = CommonUtil.addUrlPrefix(shortLinkAddRequest.getOriginalUrl());
		shortLinkAddRequest.setOriginalUrl(newUrl);
		EventMessage eventMessage = EventMessage.builder().accountNo(accountNo)
				.content(JsonUtil.obj2Json(shortLinkAddRequest))
				.messageId(IdUtil.generateId().toString())
				.eventMessageType(EventMessageType.SHORT_LINK_ADD.name())
				.build();
		rabbitTemplate.convertAndSend(rabbitMQConfig.getShortLinkAddEventExchange(),rabbitMQConfig.getShortLinkAddRoutingKey(),eventMessage);
		return JsonData.buildSuccess();
	}

	@Override
	public Boolean handlerAddShortLink(EventMessage eventMessage) {
		Long accountNo = eventMessage.getAccountNo();
		ShortLinkAddRequest shortLinkAddRequest= JsonUtil.json2Obj(eventMessage.getContent(),ShortLinkAddRequest.class);
		//短链域名校验
		DomainDO domainDO = checkDomain(accountNo,shortLinkAddRequest.getDomainId(),shortLinkAddRequest.getDomainType());
		//校验组是否合法
		LinkGroupDO linkGroupDO =checkGroup(shortLinkAddRequest.getGroupId(),accountNo);
		//长链摘要
		String url = shortLinkAddRequest.getOriginalUrl();
		//生成短链码
		String code = shortLinkComponent.getShortLink(url);
		// TODO 加锁
		String originalUrlDigest = CommonUtil.MD5(shortLinkAddRequest.getOriginalUrl());

		Boolean duplicateCodeFlag = false ;

		String script = "if redis.call('EXISTS',KEYS[1])==0 then redis.call('set',KEYS[1],ARGV[1]); redis.call('expire',KEYS[1],ARGV[2]); return 1;" +
				" elseif redis.call('get',KEYS[1]) == ARGV[1] then return 2;" +
				" else return 0; end;";

		Long result =(long) redisTemplate.execute(new  DefaultRedisScript<>(script,Long.class), Arrays.asList(code), accountNo, 100);

        if(result > 0){
			if (eventMessage.getEventMessageType().equalsIgnoreCase(EventMessageType.SHORT_LINK_ADD.name())){
		//先判断是否短链码被占用
		ShortLinkDO ShortLinCodeDOInDB = shortLinkManager.findByShortLinkCode(code);
        if(ShortLinCodeDOInDB == null){

				//C端处理
				//构建短链对象
				ShortLinkDO shortLinkDO = ShortLinkDO.builder().accountNo(accountNo)
						.code(code).domain(domainDO.getValue()).groupId(linkGroupDO.getId())
						.title(shortLinkAddRequest.getTitle()).expired(shortLinkAddRequest.getExpired())
						.sign(originalUrlDigest)
						.originalUrl(shortLinkAddRequest.getOriginalUrl())
						.state(ShortLinkStateEnum.ACTIVE.name())
						.del(0)
						.build();
				int rows = shortLinkManager.addShortLink(shortLinkDO);
				log.info("c端插入短链{}条",rows);
				return  true;
			}
		else{
			log.error("C端短链码重复:{}", eventMessage);
			duplicateCodeFlag = true ;
		}
			}
			if (eventMessage.getEventMessageType().equalsIgnoreCase(EventMessageType.SHORT_LINK_ADD_MAPPING.name())){
				GroupCodeMappingDO groupCodeMappingDODB = groupCodeMappingManager.findByCode(code);
				if(groupCodeMappingDODB== null){
				GroupCodeMappingDO groupCodeMappingDO = GroupCodeMappingDO.builder().accountNo(accountNo)
						.code(code)
						.title(shortLinkAddRequest.getTitle())
						.originalUrl(shortLinkAddRequest.getOriginalUrl())
						.domain(domainDO.getValue())
						.groupId(linkGroupDO.getId())
						.expired(shortLinkAddRequest.getExpired())
						.state(ShortLinkStateEnum.ACTIVE.name())
						.sign(originalUrlDigest)
						.del(0)
						.build();
				int rows= groupCodeMappingManager.add(groupCodeMappingDO);
				log.info("B端插入短链{}条",rows);
				return  true;
			}
				else{
					log.error("B端短链码重复:{}", eventMessage);
					duplicateCodeFlag = true ;
				}}
		//B端处理
	} else {
			log.error("加锁失败:{}", eventMessage);
			duplicateCodeFlag = true ;
		}
		if(duplicateCodeFlag){
			String newUrl = CommonUtil.addUrlPrefix(CommonUtil.removePrefix(shortLinkAddRequest.getOriginalUrl()));
			shortLinkAddRequest.setOriginalUrl(newUrl);
			eventMessage.setContent(JsonUtil.obj2Json(shortLinkAddRequest));
			handlerAddShortLink(eventMessage);
		}
        return false;
	}

	@Override
	public Boolean handlerDelShortLink(EventMessage eventMessage) {
		Long accountNo = eventMessage.getAccountNo();
		ShortLinkDelRequest shortLinkDelRequest = JsonUtil.json2Obj(eventMessage.getContent(), ShortLinkDelRequest.class);
		if (eventMessage.getEventMessageType().equalsIgnoreCase(EventMessageType.SHORT_LINK_DEL.name())) {
			int rows = shortLinkManager.del(shortLinkDelRequest.getCode(), accountNo);
		     log.debug("c端短链删除条数{}",rows);
		     return true;		}
		if (eventMessage.getEventMessageType().equalsIgnoreCase(EventMessageType.SHORT_LINK_DEL_MAPPING.name())) {
			int rows = groupCodeMappingManager.del(shortLinkDelRequest.getCode(),accountNo,shortLinkDelRequest.getGroupId());
			log.debug("b端短链删除条数{}",rows);
			return true;
		}
		return  false;
	}

	@Override
	public Boolean handlerUpdateShortLink(EventMessage eventMessage) {
		Long accountNo = eventMessage.getAccountNo();
		ShortLinkUpdateRequest shortLinkUpdateRequest = JsonUtil.json2Obj(eventMessage.getContent(), ShortLinkUpdateRequest.class);
		DomainDO domainDO = checkDomain(accountNo,shortLinkUpdateRequest.getDomainId(),shortLinkUpdateRequest.getDomainType());
		//校验组是否合法
		LinkGroupDO linkGroupDO =checkGroup(shortLinkUpdateRequest.getGroupId(),accountNo);

		if (eventMessage.getEventMessageType().equalsIgnoreCase(EventMessageType.SHORT_LINK_UPDATE.name())) {
			ShortLinkDO shortLinkDO = ShortLinkDO.builder().title(shortLinkUpdateRequest.getTitle())
					.groupId(linkGroupDO.getId()).domain(domainDO.getValue()).code(shortLinkUpdateRequest.getCode())
					.accountNo(accountNo).build();
			int rows = shortLinkManager.update(shortLinkDO);
			log.debug("c端短链更新条数{}",rows);
			return true;		}
		if (eventMessage.getEventMessageType().equalsIgnoreCase(EventMessageType.SHORT_LINK_UPDATE_MAPPING.name())) {
			GroupCodeMappingDO groupCodeMappingDO = GroupCodeMappingDO.builder()
					.id(shortLinkUpdateRequest.getMappingId()).groupId(shortLinkUpdateRequest.getGroupId())
					.accountNo(accountNo)
					.title(shortLinkUpdateRequest.getTitle())
					.domain(domainDO.getValue())
					.build();
			int rows = groupCodeMappingManager.update(groupCodeMappingDO);
			log.debug("b端短链更新条数{}",rows);
			return true;
		}
		return  false;
	}

	@Override
	public Map pageByGroupId(ShortLinkPageRequest shortLinkPageRequest) {
		Long accountNo = LoginInterceptor.threadLocal.get().getAccountNo();
		Map  map = groupCodeMappingManager.pageShortLinkByGroupId(shortLinkPageRequest.getPage(),
				shortLinkPageRequest.getSize(),accountNo,shortLinkPageRequest.getGroupId());
		return map;
	}

	@Override
	public JsonData update(ShortLinkUpdateRequest shortLinkUpdateRequest) {
		Long accountNo = LoginInterceptor.threadLocal.get().getAccountNo();
		EventMessage eventMessage = EventMessage.builder().accountNo(accountNo)
				.content(JsonUtil.obj2Json(shortLinkUpdateRequest))
				.messageId(IdUtil.generateId().toString())
				.eventMessageType(EventMessageType.SHORT_LINK_UPDATE.name())
				.build();
		rabbitTemplate.convertAndSend(rabbitMQConfig.getShortLinkUpdateEventExchange(),rabbitMQConfig.getShortLinkUpdateRoutingKey(),eventMessage);
		return JsonData.buildSuccess();
	}

	@Override
	public JsonData delete(ShortLinkDelRequest shortLinkDelRequest) {
		Long accountNo = LoginInterceptor.threadLocal.get().getAccountNo();
		EventMessage eventMessage = EventMessage.builder().accountNo(accountNo)
				.content(JsonUtil.obj2Json(shortLinkDelRequest))
				.messageId(IdUtil.generateId().toString())
				.eventMessageType(EventMessageType.SHORT_LINK_DEL.name())
				.build();
		rabbitTemplate.convertAndSend(rabbitMQConfig.getShortLinkDelEventExchange(),rabbitMQConfig.getShortLinkDelRoutingKey(),eventMessage);
		return JsonData.buildSuccess();
	}

	public DomainDO checkDomain(Long accountNo,Long domainId,String  domainType ){
		DomainDO domainDO;
		if (domainType.equalsIgnoreCase(DomainTypeEnum.OFFICIAL.name())){
			 domainDO  = domainManager.findByDomainTypeAndID(domainId,DomainTypeEnum.OFFICIAL);
		} else  {
			 domainDO = domainManager.findById(domainId,accountNo);		}
		Assert.notNull(domainDO,"短链域名不合法");
		return  domainDO;
	}

	public LinkGroupDO checkGroup(Long groupId,Long accountNo){
		LinkGroupDO linkGroupDO = linkGroupManager.getDetail(groupId,accountNo);
		Assert.notNull(linkGroupDO,"短链域名不合法");
		return  linkGroupDO;
	}

}
